package main

import (
	mgo "gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type DBOperation struct {
	session *mgo.Session
	dbName  string
}

const (
	CollectionReceivers              string = "receivers"
	CollectionSensors                string = "sensors"
	CollectionPositions              string = "positions"
	CollectionRelations              string = "relations"
	CollectionTires                  string = "tires"
	CollectionVehicles               string = "vehicles"
	CollectionOrganizationProperties string = "organizationproperties"
	CollectionMileageStatistics      string = "mileagestatistics"
)

func NewDBOperation(cfg *Config) (*DBOperation, error) {
	zLog(nil, "数据库连接::", cfg.DBUrl, " 开始")
	session, err := mgo.Dial(cfg.DBUrl)
	if err != nil {
		zLog(nil, "数据库连接失败::", err.Error())
		return nil, err
	}
	if cfg.DBUser != "" && cfg.DBPwd != "" {
		err = session.Login(&mgo.Credential{
			Username: cfg.DBUser,
			Password: cfg.DBPwd,
		})
		if err != nil {
			zLog(nil, "数据库登录失败::", err.Error())
			return nil, err
		}
	}

	session.SetPoolLimit(100)

	dbo := DBOperation{session, cfg.DBName}

	return &dbo, nil
}

func (p *DBOperation) Close() {
	p.session.Close()
}

type ReceiverT struct {
	ID_ bson.ObjectId `bson:"_id"`
	Rid string        `bson:"rid"`
}

func (p *DBOperation) getReceivers(_id bson.ObjectId, count int) ([]ReceiverT, error) {
	s := p.session.Copy()
	defer s.Close()

	query := make(bson.M, 0)
	if _id != "" {
		query["_id"] = bson.M{"$gt": _id}
	}
	var res []ReceiverT

	c := s.DB(p.dbName).C(CollectionReceivers)
	err := c.Find(query).Select(bson.M{"_id": 1, "rid": 1}).Sort("_id").Limit(count).All(&res)
	return res, err
}

type LocationT struct {
	Lat float64 `bson:"lat"`
	Lng float64 `bson:"lng"`
}

func (p *DBOperation) receiverTrack(rid string, date int) ([]LocationT, error) {
	s := p.session.Copy()
	defer s.Close()

	var res []LocationT
	c := s.DB(p.dbName).C(CollectionPositions)

	err := c.Find(bson.M{"rid": rid, "date": date}).Select(bson.M{"_id": 0, "lat": 1, "lng": 1}).Sort("time").All(&res)
	if err != nil {
		return nil, err
	}

	return res, nil
}

func (p *DBOperation) saveMileage(rid string, date int, mileage float64) {

	y := date / 10000
	m := date % 10000 / 100
	d := date % 100

	p.saveDeviceMileage("rid", rid, y, m, d, mileage)

	vid := p.getVidByRid(rid)
	if vid != "" {
		p.saveDeviceMileage("vid", vid, y, m, d, mileage)
	}

	sids := p.getRelationsByRid(rid, "sid")
	for _, v := range sids {
		p.saveDeviceMileage("sid", v, y, m, d, mileage)
	}

	tnos := p.getRelationsByRid(rid, "tno")
	for _, v := range tnos {
		p.saveDeviceMileage("tid", v, y, m, d, mileage)
	}
}

func (p *DBOperation) saveTotalMileage(rid string, totalMileage float64) {
	s := p.session.Copy()
	defer s.Close()

	var c *mgo.Collection
	mSet := bson.M{"$inc": bson.M{"ma": totalMileage}}

	c = s.DB(p.dbName).C(CollectionReceivers)
	c.Update(bson.M{"rid": rid}, mSet)

	vid := p.getVidByRid(rid)
	if vid != "" {
		c = s.DB(p.dbName).C(CollectionVehicles)
		c.Update(bson.M{"vid": vid}, mSet)
	}

	sids := p.getRelationsByRid(rid, "sid")
	if len(sids) > 0 {
		c = s.DB(p.dbName).C(CollectionSensors)
		c.UpdateAll(bson.M{"sid": bson.M{"$in": sids}}, mSet)
	}

	tnos := p.getRelationsByRid(rid, "tno")
	if len(tnos) > 0 {
		c = s.DB(p.dbName).C(CollectionTires)
		c.UpdateAll(bson.M{"tno": bson.M{"$in": tnos}}, mSet)
	}
}

func (p *DBOperation) saveDeviceMileage(key, value string, year, month, day int, mileage float64) {
	s := p.session.Copy()
	defer s.Close()

	d := year*10000 + month*100 + day

	var res map[string]bson.ObjectId
	c := s.DB(p.dbName).C(CollectionMileageStatistics)
	/*"y": year, "m": month, "d": day}*/
	err := c.Find(bson.M{key: value, "date": d}).Select(bson.M{"_id": 1}).One(&res)
	if err == nil {

		_id := res["_id"]
		c.Update(bson.M{"_id": _id}, bson.M{"$set": bson.M{"ma": mileage}})
	} else {

		c.Insert(bson.M{key: value, "date": d, "y": year, "m": month, "d": day, "ma": mileage})
	}
}

func (p *DBOperation) getVidByRid(rid string) string {
	s := p.session.Copy()
	defer s.Close()

	var res map[string]string
	c := s.DB(p.dbName).C(CollectionReceivers)
	err := c.Find(bson.M{"rid": rid}).Select(bson.M{"_id": 0, "vid": 1}).One(&res)
	if err != nil {
		return ""
	}
	return res["vid"]
}

func (p *DBOperation) getRelationsByRid(rid string, fieldName string) []string {
	s := p.session.Copy()
	defer s.Close()

	var res []map[string]string
	var ret []string

	c := s.DB(p.dbName).C(CollectionRelations)
	err := c.Find(bson.M{"rid": rid, fieldName: bson.M{"$exists": 1}}).Select(bson.M{"_id": 0, fieldName: 1}).All(&res)
	if err != nil {
		ret = make([]string, 0)
		return ret
	}
	ret = make([]string, 0, len(res))
	for _, v := range res {
		ret = append(ret, v[fieldName])
	}
	return ret
}
